﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.DataViz.NetworkDiagram.NetworkData
{
    /// <summary>
    /// all vertice tag objects must inherit from this class
    /// </summary>
    public class NetworkDataObject
    {
        #region props
        private string _EntityTypeName;
        /// <summary></summary>
        public string EntityTypeName
        {
            get
            {
                return this._EntityTypeName;
            }
            set
            {
                this._EntityTypeName = value;
            }
        }

        private string _PkFieldName;
        /// <summary></summary>
        public string PkFieldName
        {
            get
            {
                return this._PkFieldName;
            }
            set
            {
                this._PkFieldName = value;
            }
        }

        private string _LabelFieldName;
        /// <summary></summary>
        public string LabelFieldName
        {
            get
            {
                return this._LabelFieldName;
            }
            set
            {
                this._LabelFieldName = value;
            }
        }

        private string _TimeStampField;
        /// <summary>this field is used to partition data into states by time</summary>
        public string TimeStampField
        {
            get
            {
                return this._TimeStampField;
            }
            set
            {
                this._TimeStampField = value;
            }
        }

        private Dictionary<string,string> _FieldNameMappings;
        /// <summary></summary>
        public Dictionary<string,string> FieldNameMappings
        {
            get
            {
                return this._FieldNameMappings;
            }
            set
            {
                this._FieldNameMappings = value;
            }
        }

        private Dictionary<string,SimpleDataType> _FieldDataTypes;
        /// <summary></summary>
        public Dictionary<string,SimpleDataType> FieldDataTypes
        {
            get
            {
                return this._FieldDataTypes;
            }
            set
            {
                this._FieldDataTypes = value;
            }
        }

        private List<string> _SelfLinkFromFields;
        /// <summary></summary>
        public List<string> SelfLinkFromFields
        {
            get
            {
                return this._SelfLinkFromFields;
            }
            set
            {
                this._SelfLinkFromFields = value;
            }
        }

        private List<string> _SelfLinkToFields;
        /// <summary></summary>
        public List<string> SelfLinkToFields
        {
            get
            {
                return this._SelfLinkToFields;
            }
            set
            {
                this._SelfLinkToFields = value;
            }
        }

        private LinkMethod _SelfLinkMethod;
        /// <summary></summary>
        public LinkMethod SelfLinkMethod
        {
            get
            {
                return this._SelfLinkMethod;
            }
            set
            {
                this._SelfLinkMethod = value;
            }
        }

        private bool _IsLinkDirectional;
        /// <summary></summary>
        public bool IsLinkDirectional
        {
            get
            {
                return this._IsLinkDirectional;
            }
            set
            {
                this._IsLinkDirectional = value;
            }
        }

        private List<string> _FilterableFields;
        /// <summary></summary>
        public List<string> FilterableFields
        {
            get
            {
                return this._FilterableFields;
            }
            set
            {
                this._FilterableFields = value;
            }
        }

        private List<string> _LinkableFields;
        /// <summary></summary>
        public List<string> LinkableFields
        {
            get
            {
                return this._LinkableFields;
            }
            set
            {
                this._LinkableFields = value;
            }
        }

        private Dictionary<string,string> _DistanceCalcArgumentMappings;
        /// <summary>map target field name to distance calc method argument name</summary>
        public Dictionary<string,string> DistanceCalcArgumentMappings
        {
            get
            {
                return this._DistanceCalcArgumentMappings;
            }
            set
            {
                this._DistanceCalcArgumentMappings = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public NetworkDataObject()
        {
            this._EntityTypeName = string.Empty;
            this._PkFieldName = string.Empty;
            this._LabelFieldName = string.Empty;
            this._TimeStampField = string.Empty;
            this._FieldNameMappings=new Dictionary<string, string>();
            this._FieldDataTypes=new Dictionary<string, SimpleDataType>();
            this._SelfLinkFromFields=new List<string>();
            this._SelfLinkToFields=new List<string>();
            this._SelfLinkMethod = LinkMethod.Equals;
            this._IsLinkDirectional = false;
            this._FilterableFields=new List<string>();
            this._LinkableFields=new List<string>();
            this._DistanceCalcArgumentMappings=new Dictionary<string, string>();
        }
        #endregion

        #region get methods
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string GetPkValue(XmlNode dataNode)
        {
            return XmlDataUtil.GetAttributeValue(dataNode, this.PkFieldName, "");
        }

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public string GetLabel(XmlNode dataNode)
		{
		    return XmlDataUtil.GetAttributeValue(dataNode, this.LabelFieldName, "");
		}

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, SimpleDataType> GetCollectionTypeMembers()
        {
            Dictionary<string, SimpleDataType> props = new Dictionary<string, SimpleDataType>();
            if(this.FieldDataTypes !=null && this.FieldDataTypes.Count>0)
            {
                foreach(string fieldName in this.FieldDataTypes.Keys)
                {
                    if(this.FieldDataTypes[fieldName]==SimpleDataType.StringListType || 
                        this.FieldDataTypes[fieldName]==SimpleDataType.IntListType)
                    {
                        props.Add(fieldName, this.FieldDataTypes[fieldName]);
                    }
                }
            }
            return props;
        }
        #endregion

        #region meta data
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xDoc"></param>
        /// <param name="metaDataNode"></param>
        public void SaveMetaData(ref XmlDocument xDoc, XmlNode metaDataNode)
        {
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "EntityTypeName",this.EntityTypeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "PkFieldName", this.PkFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "LabelFieldName", this.LabelFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "TimeStampField",this.TimeStampField);
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "SelfLinkFromField",StringUtil.ToString(this.SelfLinkFromFields.ToArray()));
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "SelfLinkToFields",StringUtil.ToString(this.SelfLinkToFields.ToArray()));
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "SelfLinkMethod",this.SelfLinkMethod.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, metaDataNode, "LinkDirectional",this.IsLinkDirectional.ToString());

            XmlNode fieldNameMappingNodes = XmlDataUtil.AddElement(ref xDoc, metaDataNode, "FieldNameMappings");
            foreach (string srcField in this.FieldNameMappings.Keys)
            {
                XmlNode fieldNameMappingNode =
                    XmlDataUtil.AddElement(ref xDoc, fieldNameMappingNodes, "FieldNameMapping");
                XmlDataUtil.UpdateAttribute(ref xDoc, fieldNameMappingNode, "SrcFieldName", srcField);
                XmlDataUtil.UpdateAttribute(ref xDoc, fieldNameMappingNode, "TgtFieldName", this.FieldNameMappings[srcField]);
            }
            XmlNode dataTypeNodes = XmlDataUtil.AddElement(ref xDoc, metaDataNode, "FieldDataTypes");
            foreach(string fieldName in this.FieldDataTypes.Keys)
            {
                XmlNode dataTypeNode = XmlDataUtil.AddElement(ref xDoc, dataTypeNodes, "FieldDataType");
                XmlDataUtil.UpdateAttribute(ref xDoc, dataTypeNode,"FieldName",fieldName);
                XmlDataUtil.UpdateAttribute(ref xDoc, dataTypeNode, "DataType",
                                            this.FieldDataTypes[fieldName].ToString());
            }
            XmlNode filterableNodes = XmlDataUtil.AddElement(ref xDoc, metaDataNode, "FilterableFields");
            if(this.FilterableFields !=null && this.FilterableFields.Count>0)
            {
                foreach(string fieldName in this.FilterableFields)
                {
                    XmlNode filterNode = XmlDataUtil.AddElement(ref xDoc, filterableNodes, "FilterableField");
                    XmlDataUtil.UpdateAttribute(ref xDoc, filterNode, "FieldName", fieldName);
                }
            }
            XmlNode linkableNodes = XmlDataUtil.AddElement(ref xDoc, metaDataNode, "LinkableFields");
            if(this.LinkableFields !=null && this.LinkableFields.Count>0)
            {
                foreach(string fieldName in this.LinkableFields)
                {
                    XmlNode linkableNode = XmlDataUtil.AddElement(ref xDoc, linkableNodes, "LinkableField");
                    XmlDataUtil.UpdateAttribute(ref xDoc, linkableNode, "FieldName",fieldName);
                }
            }
            XmlNode distArgMappingNodes = XmlDataUtil.AddElement(ref xDoc, metaDataNode, "DistanceCalcArgMappings");
            if(this.DistanceCalcArgumentMappings !=null && this.DistanceCalcArgumentMappings.Count>0)
            {
                foreach(string fieldName in this.DistanceCalcArgumentMappings.Keys)
                {
                    XmlNode distArgMappingNode = XmlDataUtil.AddElement(ref xDoc, distArgMappingNodes,
                                                                        "DistanceCalcArgMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, distArgMappingNode, "FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, distArgMappingNode, "ArgName",this.DistanceCalcArgumentMappings[fieldName]);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="metaDataNode"></param>
        /// <returns></returns>
        public static NetworkDataObject ReadMetaData(XmlNode metaDataNode)
        {
            NetworkDataObject netMetaData=new NetworkDataObject();
            netMetaData.EntityTypeName = XmlDataUtil.GetAttributeValue(metaDataNode, "EntityTypeName", "");
            netMetaData.PkFieldName = XmlDataUtil.GetAttributeValue(metaDataNode, "PkFieldName", "");
            netMetaData.LabelFieldName = XmlDataUtil.GetAttributeValue(metaDataNode, "LabelFieldName", "");
            netMetaData.TimeStampField = XmlDataUtil.GetAttributeValue(metaDataNode, "TimeStampField", "");
            string[] linkFromFields =
                StringUtil.Split(XmlDataUtil.GetAttributeValue(metaDataNode, "SelfLinkFromFields", ""));
            if(linkFromFields !=null)
                netMetaData.SelfLinkFromFields.AddRange(linkFromFields);
            string[] linkToFields =
                StringUtil.Split(XmlDataUtil.GetAttributeValue(metaDataNode, "SelfLinkToFields", ""));
            if(linkToFields !=null)
                netMetaData.SelfLinkToFields.AddRange(linkToFields);
            netMetaData.SelfLinkMethod =
                (LinkMethod) Enum.Parse(
                                 typeof (LinkMethod),
                                 XmlDataUtil.GetAttributeValue(
                                     metaDataNode, "SelfLinkMethod",
                                     LinkMethod.Equals.ToString()));
            netMetaData.IsLinkDirectional =
                bool.Parse(XmlDataUtil.GetAttributeValue(metaDataNode, "LinkDirectional", false.ToString()));
            XmlNodeList fieldMappingNodes = metaDataNode.SelectNodes("FieldNameMappings/FieldNameMapping");
            if(fieldMappingNodes !=null && fieldMappingNodes.Count>0)
            {
                foreach(XmlNode mappingNode in fieldMappingNodes)
                {
                    string srcField = XmlDataUtil.GetAttributeValue(mappingNode, "SrcFieldName", "");
                    string tgtField = XmlDataUtil.GetAttributeValue(mappingNode, "TgtFieldName", "");
                    netMetaData.FieldNameMappings.Add(srcField,tgtField);
                }
            }
            XmlNodeList dataTypeNodes = metaDataNode.SelectNodes("FieldDataTypes/FieldDataType");
            if(dataTypeNodes !=null && dataTypeNodes.Count>0)
            {
                foreach(XmlNode dataTypeNode in dataTypeNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(dataTypeNode, "FieldName", "");
                    SimpleDataType dataType =
                        (SimpleDataType) Enum.Parse(typeof (SimpleDataType),
                                                    XmlDataUtil.GetAttributeValue(
                                                        dataTypeNode, "DataType", SimpleDataType.TextType.ToString()));
                    netMetaData.FieldDataTypes.Add(fieldName, dataType);
                }
            }
            XmlNodeList filterableNodes = metaDataNode.SelectNodes("FilterableFields/FilterableField");
            if(filterableNodes !=null && filterableNodes.Count>0)
            {
                foreach(XmlNode filterableNode in filterableNodes)
                {
                    netMetaData.FilterableFields.Add(
                        XmlDataUtil.GetAttributeValue(filterableNode,"FieldName",""));
                }
            }
            XmlNodeList linkableNodes = metaDataNode.SelectNodes("LinkableFields/LinkableField");
            if(linkableNodes !=null && linkableNodes.Count>0)
            {
                foreach(XmlNode linkableNode in linkableNodes)
                {
                    netMetaData.LinkableFields.Add(
                        XmlDataUtil.GetAttributeValue(linkableNode,"FieldName",""));
                }
            }
            XmlNodeList distArgMapNodes = metaDataNode.SelectNodes("DistanceCalcArgMappings/DistanceCalcArgMapping");
            if(distArgMapNodes !=null && distArgMapNodes.Count>0)
            {
                foreach(XmlNode distArgMapNode in distArgMapNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(distArgMapNode, "FieldName", "");
                    string argName = XmlDataUtil.GetAttributeValue(distArgMapNode, "ArgName", "");
                    netMetaData.DistanceCalcArgumentMappings.Add(fieldName, argName);
                }
            }
            return netMetaData;
        }
        #endregion

        #region data
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xDoc"></param>
        /// <param name="tableNode"></param>
        /// <param name="dtNetData"></param>
        public void SaveData(ref XmlDocument xDoc, XmlNode tableNode, DataTable dtNetData)
        {
            Dictionary<string, SimpleDataType> collectionTypeProps = this.GetCollectionTypeMembers();
            foreach(DataRow dr in dtNetData.Rows)
            {
                XmlNode rowNode = XmlDataUtil.AddElement(ref xDoc, tableNode, "Row");
                foreach(string fieldName in this.FieldDataTypes.Keys)
                {
                    if (dr[fieldName] != null && dr[fieldName] != DBNull.Value)
                    {
                        XmlNode fieldNode = XmlDataUtil.AddElement(ref xDoc, rowNode, fieldName);
                        if (collectionTypeProps.ContainsKey(fieldName))
                        {
                            if (collectionTypeProps[fieldName] == SimpleDataType.IntListType)
                            {
                                List<int> propValues = dr[fieldName] as List<int>;
                                fieldNode.InnerText = IntUtil.ToString(propValues.ToArray());
                            }
                            else
                            {
                                List<string> propValues = dr[fieldName] as List<string>;
                                fieldNode.InnerText = StringUtil.ToString(propValues.ToArray());
                            }
                        }
                        else
                        {
                            fieldNode.InnerText = dr[fieldName].ToString();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableNode"></param>
        /// <returns></returns>
        public DataTable ReadData(XmlNode tableNode)
        {
            DataTable dtData=new DataTable();
            foreach(string fieldName in this.FieldDataTypes.Keys)
            {
                dtData.Columns.Add(fieldName, SimpleDataTypeUtil.ToSystemType(this.FieldDataTypes[fieldName]));
            }
            Dictionary<string, SimpleDataType> collectionTypeProps = this.GetCollectionTypeMembers();
            XmlNodeList rowNodes = tableNode.SelectNodes("Row");
            foreach(XmlNode rowNode in rowNodes)
            {
                DataRow dr = dtData.NewRow();
                foreach(string fieldName in this.FieldDataTypes.Keys)
                {
                    XmlNode fieldNode = rowNode.SelectSingleNode(fieldName);
                    if(fieldNode !=null && fieldNode.InnerText!=null)
                    {
                        string propValue = fieldNode.InnerText;
                        if (collectionTypeProps.ContainsKey(fieldName))
                        {
                            if (collectionTypeProps[fieldName] == SimpleDataType.IntListType)
                            {
                                int[] intArray = IntUtil.ToArray(propValue);
                                List<int> propValues = new List<int>();
                                propValues.AddRange(intArray);
                                dr[fieldName] = propValues;
                            }
                            else
                            {
                                string[] intArray = StringUtil.Split(propValue);
                                List<string> propValues = new List<string>();
                                propValues.AddRange(intArray);
                                dr[fieldName] = propValues;
                            }
                        }
                        else
                        {
                            object propValue2 = SimpleDataTypeUtil.ToDbValue(this.FieldDataTypes[fieldName], propValue.ToString());
                            dr[fieldName] = propValue2;
                        }
                    }
                }
                dtData.Rows.Add(dr);
            }

            return dtData;
        }
        #endregion
    }
}
